/**************************************************************************** 
* 
* Copyright (c) 2023  C*Core -   All Rights Reserved  
* 
* THIS SOFTWARE IS DISTRIBUTED "AS IS," AND ALL WARRANTIES ARE DISCLAIMED, 
* INCLUDING MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* 
* PROJECT     : CCFC2011BC           
* DESCRIPTION : CCFC2011BC canfd low level drivers code 
* HISTORY     : Initial version
* @file     canfd_lld.c
* @version  1.1
* @date     2023 - 02 - 20
* @brief    Initial version.
*
*****************************************************************************/

#include "canfd_lld.h"

static CanfdTransBufState g_trans_buf_state[CANFD_MOD_BUTT][CANFD_TRANS_BUF_TYPE_BUTT];
static const uint8_t g_dlc_2_size_list[] =
{
    12, 16, 20, 24, 32, 48, 64,
};
static CANFDIntrCallback g_intr_callback[CANFD_MOD_BUTT];

/* common API */
/*******************************************************************************
 * @brief      Get CANFD handle though ID
 * @param[in]  CANFDModId canfd_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
CANFDDriver* Canfd_LLD_Get_Driver(CANFDModId canfd_id)
{
    static CANFDDriver g_canfd_driver[CANFD_MOD_BUTT];
    return &g_canfd_driver[canfd_id];
}

/*******************************************************************************
 * @brief      Get CANFD register base address though ID
 * @param[in]  CANFDModId canfd_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static volatile CANFDReg* Canfd_LLD_Get_Reg(CANFDModId canfd_id)
{
    static volatile CANFDReg * const reg[CANFD_MOD_BUTT] =
    {
        (volatile CANFDReg*)&CANFD_0,
        (volatile CANFDReg*)&CANFD_1,
        (volatile CANFDReg*)&CANFD_2,
        (volatile CANFDReg*)&CANFD_3,
        (volatile CANFDReg*)&CANFD_4,
        (volatile CANFDReg*)&CANFD_5,
    };
    return reg[canfd_id];
}

/*******************************************************************************
 * @brief      Set CANFD transmission speed
 * @param[in]  volatile CANFDReg *reg
 *             const CANFDConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Canfd_LLD_Set_Rate(volatile CANFDReg *reg, const CANFDConfig *config)
{
    static const CANFDTimeSeg time_seg[CANFD_BIT_RATE_BUTT] =
    {
        /* Seg_1, Seg_2, SJW, SSP, PRESC */
        {15, 1, 4, 15, 0}, // CANFD_MBPS_8
        {15, 1, 4, 15, 1}, // CANFD_MBPS_4
        {15, 1, 4, 15, 2}, // CANFD_MBPS_2
        {15, 1, 4, 15, 4}, // CANFD_MBPS_1
        {15, 1, 4, 15, 8}, // CANFD_KBPS_500
        {32, 7, 6, 32, 4}, // CANFD_KBPS_400
        {32, 7, 6, 32, 8}, // CANFD_KBPS_200
        {7, 1, 4, 7, 64}, // CANFD_KBPS_125
        {65, 15, 4, 65, 8}, // CANFD_KBPS_100
    };

    /* second sample point offset */
    reg->ERRINFO.B.SSPOFF = time_seg[config->phy_para.preamble_bit_rate].ssp;

    /* T Seg_1 = (S_Seg_1 + 2) * TQ */
    reg->SBITRATE.B.S_Seg_1 = time_seg[config->phy_para.preamble_bit_rate].seg1 - 2U;
    /* T Seg_2 = (S_Seg_2 + 1) * TQ */
    reg->SBITRATE.B.S_Seg_2 = time_seg[config->phy_para.preamble_bit_rate].seg2 - 1U;
    /* T SJW = (S_SJW + 1) * TQ */
    reg->SBITRATE.B.S_SJW = time_seg[config->phy_para.preamble_bit_rate].sjw - 1U;
    /* prescaler = S_PRESC + 1 */
    reg->SBITRATE.B.S_PRESC = time_seg[config->phy_para.preamble_bit_rate].prescaler - 1U;

    reg->FBITRATE.B.F_Seg_1 = time_seg[config->phy_para.data_bit_rate].seg1 - 2U;
    reg->FBITRATE.B.F_Seg_2 = time_seg[config->phy_para.data_bit_rate].seg2 - 1U;
    reg->FBITRATE.B.F_SJW = time_seg[config->phy_para.data_bit_rate].sjw - 1U;
    reg->FBITRATE.B.F_PRESC = time_seg[config->phy_para.data_bit_rate].prescaler - 1U;
}

/*******************************************************************************
 * @brief      Init CANFD IO port
 * @param[in]  CANFDModId can_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Canfd_LLD_Init_Port(CANFDModId can_id)
{
    static const struct
    {
        uint32_t tx_id;
        uint32_t tx_val;
        uint32_t rx_id;
        uint32_t rx_val;
    } pcr_cfg[CANFD_MOD_BUTT] =
    {
        /* CAM_MOD_ID_0: port B0 as CAN0TX, open drain ; port B1 as CAN0RX */
        {16, 0x0604, 17, 0x0100},
        /* CAM_MOD_ID_1: port C10 as CAN1TX, open drain ; port C11 as CAN1RX */
        {42, 0x0604, 43, 0x0100},
        /* CAM_MOD_ID_2: port E8  as CAN2TX, open drain ; port E9  as CAN2RX */
        {72, 0x0604, 73, 0x0100},
        /* CAM_MOD_ID_3: port C5  as CAN3TX, open drain ; port C4  as CAN3RX */
        {37, 0x0A04, 36, 0x0100},
        /* CAM_MOD_ID_4: port C2  as CAN4TX, open drain ; port C3  as CAN4RX */
        {34, 0x0A04, 35, 0x0100},
        /* CAM_MOD_ID_5: port E1  as CAN5TX, open drain ; port E0  as CAN5RX */
        {65, 0x0A04, 64, 0x0100},
    };

    SIU.PCR[pcr_cfg[can_id].tx_id].R = (uint16_t)pcr_cfg[can_id].tx_val;
    SIU.PCR[pcr_cfg[can_id].rx_id].R = (uint16_t)pcr_cfg[can_id].rx_val;
    if (can_id == CANFD_MOD_ID_1)
    {
        SIU.PSMI[0].B.PADSEL = 0x01; /* select PC11 as FLEXCAN1 RX*/
    }
}

/*******************************************************************************
 * @brief      Check CANFD configuration
 * @param[in]  const CANFDConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static uint32_t Canfd_LLD_Cfg_Check(const CANFDConfig *config)
{
    if (config->canfd_id >= CANFD_MOD_BUTT)
    {
        return CANFD_ERROR_INVALID_PARA;
    }

    if ((config->phy_para.preamble_bit_rate >= CANFD_BIT_RATE_BUTT) ||
        (config->phy_para.data_bit_rate >= CANFD_BIT_RATE_BUTT))
    {
        return CANFD_ERROR_INVALID_PARA;
    }
    return CANFD_OK;
}

/*******************************************************************************
 * @brief      Config CANFD frame filter
 * @param[in]  volatile CANFDReg *reg
 *             const CANFDConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Canfd_LLD_Config_Filter(volatile CANFDReg *reg, const CANFDConfig *config)
{
    uint32_t idx;

    for (idx = 0; idx < CANFD_MAX_FILTER_NUM; idx++)
    {
        if (config->filter[idx].is_valid == 0U)
        {
            continue;
        }
        reg->ACFCTRL.B.ACFADR = idx;
        reg->ACFCTRL.B.SELMASK = 0U; /* ACF_x register points to the verification code */
        reg->ACF.B.ACODE = config->filter[idx].id;
        reg->ACFCTRL.B.SELMASK = 1U; /* ACF_x register points to the receive mask */
        reg->ACF.B.ACODE = ~config->filter[idx].mask; /* negation */
        reg->ACFCTRL.B.ACFENx |= ((uint16_t)(1U << idx)); /* PRQA S 4391 # legal type conversion */
    }
    return;
}

/*******************************************************************************
 * @brief      Select CANFD run mode
 * @param[in]  CANFDDriver *canfd
 *             volatile CANFDReg *reg
 *             const CANFDConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Canfd_LLD_Select_Run_Mode(CANFDDriver *canfd, volatile CANFDReg *reg, const CANFDConfig *config)
{
    if (config->func_switch.inner_loop_back_en == ENABLE)
    {
        /* inner loop back mode */
        reg->CTRL0.B.LBMI = ENABLE;
        canfd->state.cur_mode = CANFD_CUR_MODE_INNER_LOOP_BACK;
    }
    else if (config->func_switch.outer_loop_back_en == ENABLE)
    {
        /* outer loop back mode */
        reg->CTRL0.B.LBME = ENABLE;
        canfd->state.cur_mode = CANFD_CUR_MODE_OUTER_LOOP_BACK;
    }
    else if (config->func_switch.listen_only_en == ENABLE)
    {
        /* listen_only */
        reg->CTRL0.B.LOM = ENABLE;
        canfd->state.cur_mode = CANFD_CUR_MODE_LISTEN_ONLY;
    }
    else
    {
        canfd->state.cur_mode = CANFD_CUR_MODE_NORMAL;
    }
}

/*******************************************************************************
 * @brief      Bind CANFD Message to transmission buffer
 * @param[in]  CANFDModId canfd_id
 *             uint32_t buf_idx
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static uint32_t Canfd_LLD_Bind_Msg_Id_To_Trans_Buf(CANFDModId canfd_id, uint32_t buf_idx)
{
    static CanfdTransBufMsgId msg_id;

    msg_id.attr.msg_cnt++;
    msg_id.attr.is_valid = TRUE;
    g_trans_buf_state[canfd_id][buf_idx].cur_msg = msg_id.id;

    return g_trans_buf_state[canfd_id][buf_idx].cur_msg;
}

/*******************************************************************************
 * @brief      Get CANFD Message in transmission buffer
 * @param[in]  CANFDModId canfd_id
 *             uint32_t buf_idx
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static uint32_t Canfd_LLD_Get_Msg_Id_From_Trans_Buf(CANFDModId canfd_id, uint32_t buf_idx)
{
    return g_trans_buf_state[canfd_id][buf_idx].cur_msg;
}

/*******************************************************************************
 * @brief      Clear CANFD Message in transmission buffer
 * @param[in]  CANFDModId canfd_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Canfd_LLD_Clear_Msg_Id_In_Trans_Buf(CANFDModId canfd_id, uint32_t buf_idx)
{
    g_trans_buf_state[canfd_id][buf_idx].cur_msg = 0;
}

/*******************************************************************************
 * @brief      Convert CANFD Message length to DLC field
 * @param[in]  uint32_t data_size
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static uint32_t Canfd_LLD_Convert_Size2DLC(uint32_t data_size)
{
    uint32_t idx;

    /* within 8 bytes, the DLC value is the same as the data length */
    if (data_size < (uint32_t)CANFD_DLC_12_BYTES)
    {
        return data_size;
    }

    for (idx = 0;
        idx < (sizeof(g_dlc_2_size_list) / sizeof(g_dlc_2_size_list[0])); idx++)
    {
        if (g_dlc_2_size_list[idx] == data_size)
        {
            return idx + (uint32_t)CANFD_DLC_12_BYTES;
        }
    }

    return 0;
}

/*******************************************************************************
 * @brief      Convert DLC field to CANFD Message length 
 * @param[in]  uint8_t dlc_type
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static uint8_t Canfd_LLD_Convert_DLC2Size(uint8_t dlc_type)
{
    if (dlc_type < (uint32_t)CANFD_DLC_12_BYTES)
    {
        return dlc_type;
    }
    else if (dlc_type >= (uint32_t)CANFD_DLC_TYPE_BUTT)
    {
        return 0;
    }
    else
    {
        return g_dlc_2_size_list[dlc_type - (uint32_t)CANFD_DLC_12_BYTES];
    }
}

/*******************************************************************************
 * @brief      Fill in CANFD Message
 * @param[in]  volatile CANFDReg *reg
 *             const CANFDTxMsg *tx_msg
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Canfd_LLD_Fill_Msg(volatile CANFDReg *reg, const CANFDTxMsg *tx_msg)
{
    uint32_t byte_idx, word_idx;

    reg->TBUF.ID_TTSEN.B.TISEN = tx_msg->is_time_stamp_en;
    reg->TBUF.ID_TTSEN.B.ID = tx_msg->id;
    reg->TBUF.TXCTRL.B.DLC = Canfd_LLD_Convert_Size2DLC(tx_msg->data_len);
    reg->TBUF.TXCTRL.B.BRS = tx_msg->is_bit_rate_switch;
    reg->TBUF.TXCTRL.B.RTR = tx_msg->is_remote;
    reg->TBUF.TXCTRL.B.IDE = tx_msg->is_extern;
    reg->TBUF.TXCTRL.B.FDF = tx_msg->is_fd_format_flag;

    for (byte_idx = 0, word_idx = 0; /* PRQA S 2462, 2463, 3418 # associated index */
        (word_idx * sizeof(uint32_t)) < tx_msg->data_len;
        byte_idx += sizeof(uint32_t), word_idx++) /* PRQA S 3387, 3418 # associated index */
    {
        reg->TBUF.DATA[word_idx] = tx_msg->data.word[word_idx];
    }
}

/*******************************************************************************
 * @brief      Launch CANFD Message transmission
 * @param[in]  const CANFDDriver *canfd
 *             const CANFDTxMsg *tx_msg
 *             uint32_t *msg_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t Canfd_LLD_Send_Msg(const CANFDDriver *canfd, const CANFDTxMsg *tx_msg, uint32_t *msg_id)
{
    volatile CANFDReg *reg = Canfd_LLD_Get_Reg(canfd->id);

    switch (tx_msg->trans_buf)
    {
        case CANFD_TRANS_BUF_TYPE_PRIMARY :
        {
            if (reg->CTRL0.B.TPE != 0U)
            {
                return CANFD_ERROR_TRANS_BUF_BUSY;
            }
            reg->CTRL0.B.TBSEL = (uint8_t)CANFD_TRANS_BUF_TYPE_PRIMARY;
            Canfd_LLD_Fill_Msg(reg, tx_msg);
            reg->CTRL0.B.TPE = ENABLE;
            break;
        }
        case CANFD_TRANS_BUF_TYPE_SECONDARY :
        {
            if ((reg->CTRL0.B.TSONE != 0U) || (reg->CTRL0.B.TSALL != 0U))
            {
                return CANFD_ERROR_TRANS_BUF_BUSY;
            }
            reg->CTRL0.B.TBSEL = (uint8_t)CANFD_TRANS_BUF_TYPE_SECONDARY;
            Canfd_LLD_Fill_Msg(reg, tx_msg);
            reg->CTRL0.B.TSNEXT = ENABLE;
            reg->CTRL0.B.TSONE = ENABLE;
            break;
        }
        default :
        {
            break;
        }
    }

    *msg_id = Canfd_LLD_Bind_Msg_Id_To_Trans_Buf(canfd->id, (uint32_t)tx_msg->trans_buf);

    return CANFD_OK;
}

/*******************************************************************************
 * @brief      Check if a transmission is finished.
 * @param[in]  CANFDDriver *canfd
 *             uint32_t msg_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
uint8_t Canfd_LLD_Is_Msg_Transferred(const CANFDDriver *canfd, uint32_t msg_id)
{
    uint32_t idx;

    for (idx = 0; idx < (uint32_t)CANFD_TRANS_BUF_TYPE_BUTT; idx++)
    {
        if (Canfd_LLD_Get_Msg_Id_From_Trans_Buf(canfd->id, idx) == msg_id)
        {
            return FALSE;
        }
    }

    return TRUE;
}

/*******************************************************************************
 * @brief      Abort an CANFD frame transmission requests.
 * @param[in]  CANFDDriver *canfd
 *             uint32_t msg_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
uint8_t Canfd_LLD_Abort_Msg_Transfer(const CANFDDriver *canfd, uint32_t msg_id)
{
    uint32_t buf_idx;
    volatile CANFDReg *reg = NULL;

    for (buf_idx = 0; buf_idx < (uint32_t)CANFD_TRANS_BUF_TYPE_BUTT; buf_idx++)
    {
        if (Canfd_LLD_Get_Msg_Id_From_Trans_Buf(canfd->id, buf_idx) == msg_id)
        {
            break;
        }
    }
    if (buf_idx == (uint32_t)CANFD_TRANS_BUF_TYPE_BUTT)
    {
        return CANFD_ERROR_ABORT_MSG_TRANS;
    }

    reg = Canfd_LLD_Get_Reg(canfd->id);

    switch (buf_idx)
    {
        case (uint32_t)CANFD_TRANS_BUF_TYPE_PRIMARY :
        {
            reg->CTRL0.B.TPA = ENABLE;
            if (reg->CTRL0.B.TPA != ENABLE)
            {
                return CANFD_ERROR_ABORT_MSG_TRANS;
            }
            break;
        }
        case (uint32_t)CANFD_TRANS_BUF_TYPE_SECONDARY :
        {
            reg->CTRL0.B.TSA = ENABLE;
            if (reg->CTRL0.B.TSA != ENABLE)
            {
                return CANFD_ERROR_ABORT_MSG_TRANS;
            }
            break;
        }
        default :
        {
            break;
        }
    }

    Canfd_LLD_Clear_Msg_Id_In_Trans_Buf(canfd->id, buf_idx);

    return CANFD_OK;
}

/* receive messages */
/*******************************************************************************
 * @brief      Get an CANFD frame from Rx FIFO.
 * @param[in]  CANFDDriver *canfd
 * @param[out] CANFDRxMsg *rx_msg
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t Canfd_LLD_Recv_Msg(const CANFDDriver *canfd, CANFDRxMsg *rx_msg)
{
    volatile CANFDReg *reg = NULL;
    uint32_t byte_idx, word_idx;

    reg = Canfd_LLD_Get_Reg(canfd->id);

    if (reg->CTRL0.B.RSTAT == 0U)
    {
        return CANFD_ERROR_NO_MSG_RECEIVED;
    }

    /* Read message */
    rx_msg->id  = reg->RBUF.ID_ESI.B.ID;
    rx_msg->is_canfd = reg->RBUF.RXCTRL.B.FDF;
    rx_msg->is_remote = reg->RBUF.RXCTRL.B.RTR;
    rx_msg->is_extern = reg->RBUF.RXCTRL.B.IDE;
    rx_msg->is_bit_rate_changed = reg->RBUF.RXCTRL.B.BRS;
    rx_msg->timestamp = (uint16_t)reg->RBUF.RTS[0];
    rx_msg->data_len = Canfd_LLD_Convert_DLC2Size(reg->RBUF.RXCTRL.B.DLC);

    for (byte_idx = 0, word_idx = 0; /* PRQA S 2462, 2463, 3418 # associated index */
        (word_idx * sizeof(uint32_t)) < rx_msg->data_len;
        byte_idx += sizeof(uint32_t), word_idx++) /* PRQA S 3387, 3418 # associated index */
    {
        rx_msg->data.word[word_idx] = reg->RBUF.DATA[word_idx];
    }

    reg->CTRL0.B.RREL = 1U; /* write-release */

    return CANFD_OK;
}

/* low power */
/*******************************************************************************
 * @brief      enter to sleep mode.
 * @param[in]  CANFDDriver *canfd
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Canfd_LLD_Sleep(CANFDDriver *canfd)
{
    volatile CANFDReg *reg = NULL;

    /* fetch register */
    reg = Canfd_LLD_Get_Reg(canfd->id);
    /* enter the standby mode */
    reg->CTRL0.B.STBY = ENABLE;
    /* wait feedback */
    while (reg->CTRL0.B.STBY != ENABLE)
    {
        ;
    }
    /* update status */
    canfd->state.cur_mode = CANFD_CUR_MODE_SLEEP;
}

/*******************************************************************************
 * @brief      Wake up from sleep mode.
 * @param[in]  CANFDDriver *canfd
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Canfd_LLD_Wakeup(CANFDDriver *canfd)
{
    volatile CANFDReg *reg = NULL;

    /* fetch register */
    reg = Canfd_LLD_Get_Reg(canfd->id);
    /* exit the standby mode */
    reg->CTRL0.B.STBY = DISABLE;
    /* wait feedback */
    while (reg->CTRL0.B.STBY != DISABLE)
    {
        ;
    }
    /* update status */
    canfd->state.cur_mode = CANFD_CUR_MODE_NORMAL;
}

/*******************************************************************************
 * @brief      hook CANFD interrupt callback function
 * @param[in]  volatile CANFDReg *reg
 *             const CANFDConfig *config
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Canfd_LLD_Init_Intr(volatile CANFDReg *reg, const CANFDConfig *config)
{
    cc_memcpy((uint8_t *)&g_intr_callback[(uint32_t)config->canfd_id], /* PRQA S 2852, 2897, 2932 # legal type conversion */
        (const uint8_t *)&config->call_back, sizeof(CANFDIntrCallback));

    switch (config->canfd_id)
    {
        case CANFD_MOD_ID_0 :
        {
            INTC_InstallINTCInterruptHandler(CANFD_LLD_INTR_HANDLER_NAME(CANFD_MOD_ID_0),
                    CANFD_MOD_ID_0_INT_NUM, CAN_FLEXCAN0_IRQ_PRIORITY);
            break;
        }
        case CANFD_MOD_ID_1 :
        {
            INTC_InstallINTCInterruptHandler(CANFD_LLD_INTR_HANDLER_NAME(CANFD_MOD_ID_1),
                    CANFD_MOD_ID_1_INT_NUM, CAN_FLEXCAN1_IRQ_PRIORITY);
            break;
        }
        case CANFD_MOD_ID_2 :
        {
            INTC_InstallINTCInterruptHandler(CANFD_LLD_INTR_HANDLER_NAME(CANFD_MOD_ID_2),
                    CANFD_MOD_ID_2_INT_NUM, CAN_FLEXCAN2_IRQ_PRIORITY);
            break;
        }
        case CANFD_MOD_ID_3 :
        {
            INTC_InstallINTCInterruptHandler(CANFD_LLD_INTR_HANDLER_NAME(CANFD_MOD_ID_3),
                    CANFD_MOD_ID_3_INT_NUM, CAN_FLEXCAN3_IRQ_PRIORITY);
            break;
        }
        case CANFD_MOD_ID_4 :
        {
            INTC_InstallINTCInterruptHandler(CANFD_LLD_INTR_HANDLER_NAME(CANFD_MOD_ID_4),
                    CANFD_MOD_ID_4_INT_NUM, CAN_FLEXCAN4_IRQ_PRIORITY);
            break;
        }
        case CANFD_MOD_ID_5 :
        {
            INTC_InstallINTCInterruptHandler(CANFD_LLD_INTR_HANDLER_NAME(CANFD_MOD_ID_5),
                    CANFD_MOD_ID_5_INT_NUM, CAN_FLEXCAN5_IRQ_PRIORITY);
            break;
        }
        default :
        {
            break;
        }
    }

    /* enable interrupt */
    reg->CTRL1.B.EIE = ENABLE; /* Error Interrupt */
    reg->CTRL1.B.TSIE = ENABLE; /* Transmission Secondary */
    reg->CTRL1.B.TPIE = ENABLE; /* Transmission Primary */
    reg->CTRL1.B.RIE = ENABLE; /* receive interrupt */
}

/*******************************************************************************
 * @brief      CANFD interrupt callback function
 * @param[in]  CANFDModId canfd_id
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void Canfd_LLD_Intr_Callback(CANFDModId canfd_id)
{
    uint32_t ret;
    CANFDRxMsg rx_msg;
    CANFDDriver *canfd = Canfd_LLD_Get_Driver(canfd_id);
    volatile CANFDReg *reg = Canfd_LLD_Get_Reg(canfd_id);

    if (reg->CTRL1.B.TPIF != 0U)
    {
        Canfd_LLD_Clear_Msg_Id_In_Trans_Buf(canfd_id, (uint32_t)CANFD_TRANS_BUF_TYPE_PRIMARY);
        if (g_intr_callback[canfd_id].tx_transferred != NULL)
        {
            g_intr_callback[canfd_id].tx_transferred(canfd, CANFD_TRANS_BUF_TYPE_PRIMARY);
        }
    }
    if (reg->CTRL1.B.TSIF != 0U)
    {
        Canfd_LLD_Clear_Msg_Id_In_Trans_Buf(canfd_id, (uint32_t)CANFD_TRANS_BUF_TYPE_SECONDARY);
        if (g_intr_callback[canfd_id].tx_transferred != NULL)
        {
            g_intr_callback[canfd_id].tx_transferred(canfd, CANFD_TRANS_BUF_TYPE_SECONDARY);
        }
    }
    if (reg->CTRL1.B.RIF != 0U)
    {
        if (g_intr_callback[canfd_id].rx_received != NULL)
        {
            cc_memset((uint8_t *)&rx_msg, 0, sizeof(rx_msg));
            ret = Canfd_LLD_Recv_Msg(canfd, &rx_msg);
            if (ret == CANFD_OK)
            {
                g_intr_callback[canfd_id].rx_received(canfd, &rx_msg);
            }
        }
    }
    if (reg->CTRL1.B.EIF != 0U)
    {
        if (g_intr_callback[canfd_id].error != NULL)
        {
            g_intr_callback[canfd_id].error(canfd);
        }
    }
    /* Uniformly clear interrupt flags */
    reg->CTRL1.B.TPIF = 1U;
    reg->CTRL1.B.TSIF = 1U;
    reg->CTRL1.B.RIF = 1U;
    reg->CTRL1.B.EIF = 1U;
}

/* CANFD Diag */
/*******************************************************************************
 * @brief      Update CANFD TX and RX error counter.
 * @param[in]  CANFDDriver *canfd
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void Canfd_LLD_Update_Error_Counter(CANFDDriver *canfd)
{
    const volatile CANFDReg * const reg = Canfd_LLD_Get_Reg(canfd->id);

    canfd->state.cur_error.rx_error_cnt = reg->ERRINFO.B.RECNT;
    canfd->state.cur_error.tx_error_cnt = reg->ERRINFO.B.TECNT;
}

/* CANFD init */
/*******************************************************************************
 * @brief      Initializes the CANFD peripheral bnase on the specified parameters.
 * @param[in]  const CANFDConfig *config
 * @param[out] CANDriver *canfd
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t Canfd_LLD_Init(CANFDDriver **canfd, const CANFDConfig *config)
{
    uint32_t ret;
    volatile CANFDReg *reg = NULL;

    /* check configuration */
    ret = Canfd_LLD_Cfg_Check(config);
    if (ret != CANFD_OK)
    {
        return ret;
    }
    /* mount descriptor */
    *canfd = Canfd_LLD_Get_Driver(config->canfd_id);
    (*canfd)->id = config->canfd_id;
    /* check status */
    if ((*canfd)->state.cur_mode != CANFD_CUR_MODE_INIT)
    {
        return CANFD_ERROR_INVALID_STATE;
    }
    /* enter CANFD mode */
    Canfd_LLD_Switch_In();
    /* mapp port */
    Canfd_LLD_Init_Port((*canfd)->id);
    /* fetch register */
    reg = Canfd_LLD_Get_Reg((*canfd)->id);
    /* select clock source */
    reg->CTRL0.B.CLKSEL = config->phy_para.use_bus_clk_src;
    reg->CTRL0.B.IPGSTOP = ENABLE;
    /* hardware reset */
    reg->CTRL0.B.RESET = ENABLE;
    /* config bit rate */
    Canfd_LLD_Set_Rate(reg, config);
    /* latency compensation */
    reg->ERRINFO.B.TDCEN = config->func_switch.tdc_en;
    /* filtering rule */
    Canfd_LLD_Config_Filter(reg, config);
    /* sending mode */
    reg->CTRL0.B.TSSS = DISABLE;
    reg->CTRL0.B.TSMODE = 0; /* 0->FIFO mode 1->priority decision mode */
    /* receiving mode */
    reg->CTRL0.B.ROM = 0; /* 0->overwrite the oldest message 1->discard new message */
    /* timestamp configuration */
    reg->ACFCTRL.B.TIMEPOS = 0U;
    reg->ACFCTRL.B.TIMEEN = ENABLE;
    /* interrupt configuration */
    Canfd_LLD_Init_Intr(reg, config);
    /* start running */
    reg->CTRL0.B.RESET = DISABLE;
    /* select mode */
    Canfd_LLD_Select_Run_Mode(*canfd, reg, config);
    return CANFD_OK;
}

/*******************************************************************************
 * @brief      Deinitializes the CAN peripheral.
 * @param[in]  CANDriver *canfd
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t Canfd_LLD_DeInit(CANFDDriver *canfd)
{
    volatile CANFDReg *reg = NULL;

    if (canfd->state.cur_mode == CANFD_CUR_MODE_INIT)
    {
        return CANFD_ERROR_INVALID_STATE;
    }
    /* fetch register */
    reg = Canfd_LLD_Get_Reg(canfd->id);
    /* hardware reset */
    reg->CTRL0.B.RESET = ENABLE;
    /* clean handle */
    cc_memset((uint8_t *)canfd, 0, sizeof(CANFDDriver));
    /* select mode */
    canfd->state.cur_mode = CANFD_CUR_MODE_INIT;
    /* exit CANFD mode */
    Canfd_LLD_Switch_Out();

    return CANFD_OK;
}

